package com.recommended.service.impl;

import com.alibaba.fastjson.JSON;
import com.recommended.constants.WeChatConstant;
import com.recommended.domain.ProfessionalInfo;
import com.recommended.domain.UniversityInfo;
import com.recommended.dto.ArticleItem;
import com.recommended.dto.ReturnResult;
import com.recommended.service.WeChatService;
import com.recommended.util.DataRecommendedUtil;
import com.recommended.util.WeChatUtil;
import org.apache.mahout.cf.taste.common.TasteException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.atomic.LongAdder;
import java.util.regex.Pattern;

/**
 * @author Administrator
 */
@Service
public class WeChatServiceImpl implements WeChatService {
    private static final Logger logger = LoggerFactory.getLogger(WeChatServiceImpl.class);
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private DataRecommendedUtil dataRecommendedUtil;

    private static Pattern pattern = Pattern.compile("^[\u4e00-\u9fa5]+[\\,|\\，][\u4e00-\u9fa5]+$");

    private static final String EN_COMMA = ",";
    private static final String CN_COMMA = "，";

    private static LongAdder longAdder = new LongAdder();

    @Override
    public String processRequest(HttpServletRequest request) {
        longAdder.increment();
        // 返回xml格式的消息数据
        String respXml = null;
        try {
            // 调用parseXml方法解析请求消息
            Map<String, String> requestMap = WeChatUtil.parseXml(request);
            // 消息类型
            String msgType = requestMap.get(WeChatConstant.MsgType);
            if (msgType.equals(WeChatConstant.REQ_MESSAGE_TYPE_TEXT)) {
                // 文本消息
                respXml = text(requestMap);
            } else if (msgType.equals(WeChatConstant.REQ_MESSAGE_TYPE_EVENT)) {
                // 事件推送
                respXml = event(requestMap);
            }
        } catch (Exception e) {
            logger.error("微信请求出错:{}", e.getMessage());
        }
        return respXml;
    }

    /**
     * 处理文本消息
     *
     * @param requestMap
     * @return
     */
    private String text(Map<String, String> requestMap) {
        String respXml = "";
        // 文本消息
        String msg = requestMap.get(WeChatConstant.Content).trim();
        List<ArticleItem> items = new ArrayList<>();
        String ids = null;
        if (!StringUtils.isEmpty(msg)) {
            if (isPattern(msg)) {
                // 资料推荐，搜索专业
                Map<Integer, Integer> map = searchProfessionalMap(msg);
                if (map.size() != 0) {
                    ids = recommendedDataInfoByMap(longAdder.longValue(), map);
                }
            } else {
                Set<Integer> professionalIds = searchProfessionalSet(msg);
                if (professionalIds.size() > 0) {
                    ids = recommendedDataInfoByProfessional(longAdder.longValue(), professionalIds);
                }
            }
            if (ids != null) {
                // 推荐资料
                ArticleItem item = new ArticleItem();
                item.setTitle("为您推荐以下资料");
                item.setDescription("点击查看推荐详情");
                item.setUrl(WeChatConstant.NET_APP_URL + "/dataInfo/list?ids=" + ids);
                items.add(item);
            } else {
                // 搜索资料
                ArticleItem item = new ArticleItem();
                item.setTitle("为您搜索到以下资料");
                item.setDescription("点击查看搜索详情");
                item.setUrl(WeChatConstant.NET_APP_URL + "/dataInfo/list?keyword=" + msg);
                items.add(item);
            }
            respXml = WeChatUtil.sendArticleMsg(requestMap, items);
        }
        return respXml;
    }

    /**
     * 处理事件消息
     *
     * @param requestMap
     * @return
     */
    private String event(Map<String, String> requestMap) {
        String respXml = "";
        // 事件类型
        String eventType = requestMap.get(WeChatConstant.Event);
        // 关注
        if (eventType.equals(WeChatConstant.EVENT_TYPE_SUBSCRIBE)) {
            respXml = WeChatUtil.sendTextMsg(requestMap, buildFocusMsg());
        } else if (eventType.equals(WeChatConstant.EVENT_TYPE_UNSUBSCRIBE)) {
            // 取消关注
            // TODO 取消订阅后用户不会再收到公众账号发送的消息，因此不需要回复
        }
        return respXml;
    }

    /**
     * 搜索专业、学校对应id
     *
     * @return
     */
    private Map<Integer, Integer> searchProfessionalMap(String keyword) {
        // key:专业id，value：学校id
        Map<Integer, Integer> map = new HashMap<>();
        if (isPattern(keyword)) {
            String[] keys = new String[2];
            if (keyword.indexOf(EN_COMMA) != -1) {
                keys = keyword.split(EN_COMMA);
            } else {
                keys = keyword.split(CN_COMMA);
            }
            String professional = keys[0];
            String university = keys[1];
            MultiValueMap<String, Object> professionalParams = new LinkedMultiValueMap<>();
            professionalParams.add("keyword", professional);
            MultiValueMap<String, Object> universityParams = new LinkedMultiValueMap<>();
            universityParams.add("keyword", university);
            ReturnResult professionalResult = restTemplate.postForObject(WeChatConstant.PROFESSIONAL_SEARCH_URL, professionalParams, ReturnResult.class);
            ReturnResult universityResult = restTemplate.postForObject(WeChatConstant.UNIVERSITY_SEARCH_URL, universityParams, ReturnResult.class);
            List<ProfessionalInfo> professionalInfoList = JSON.parseArray(JSON.toJSONString(professionalResult.getData()), ProfessionalInfo.class);
            List<UniversityInfo> universityInfoList = JSON.parseArray(JSON.toJSONString(universityResult.getData()), UniversityInfo.class);
            for (ProfessionalInfo professionalInfo : professionalInfoList) {
                for (UniversityInfo universityInfo : universityInfoList) {
                    if (professionalInfo.getUniversityId().equals(universityInfo.getId())) {
                        map.put(professionalInfo.getId(), universityInfo.getId());
                        break;
                    }
                }
            }
        }
        return map;
    }

    /**
     * 搜索专业id
     *
     * @param keyword
     * @return
     */
    private Set<Integer> searchProfessionalSet(String keyword) {
        Set<Integer> set = new HashSet<>();
        MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
        params.add("keyword", keyword);
        params.add("filter", true);
        ReturnResult professionalResult = restTemplate.postForObject(WeChatConstant.PROFESSIONAL_SEARCH_URL, params, ReturnResult.class);
        List<ProfessionalInfo> professionalInfoList = JSON.parseArray(JSON.toJSONString(professionalResult.getData()), ProfessionalInfo.class);
        for (ProfessionalInfo professionalInfo : professionalInfoList) {
            set.add(professionalInfo.getId());
        }
        return set;
    }

    /**
     * 字符串是否匹配
     *
     * @param str
     * @return
     */
    private boolean isPattern(String str) {
        return pattern.matcher(str).matches();
    }

    /**
     * 根据专业和学校进行推荐，返回推荐的资料id
     *
     * @param map
     */
    private String recommendedDataInfoByMap(long currentUser, Map<Integer, Integer> map) {
        Set<Long> dataInfoIds = new HashSet<>();
        Iterator<Map.Entry<Integer, Integer>> iterator = map.entrySet().iterator();
        while (iterator.hasNext()) {
            try {
                Map.Entry<Integer, Integer> entry = iterator.next();
                List<Long> userBasedRecommendedResult = dataRecommendedUtil.userBasedRecommended(currentUser, 5, entry.getKey(), entry.getValue());
                List<Long> itemBasedRecommendedResult = dataRecommendedUtil.itemBasedRecommended(currentUser, 5, entry.getKey(), entry.getValue());
                dataInfoIds.addAll(userBasedRecommendedResult);
                dataInfoIds.addAll(itemBasedRecommendedResult);
            } catch (TasteException e) {
                e.printStackTrace();
            }
        }
        return dataInfoIdsToStr(dataInfoIds);
    }

    /**
     * 根据专业进行推荐，返回推荐的资料id
     *
     * @param currentUser
     * @param professionalIds
     * @return
     */
    private String recommendedDataInfoByProfessional(long currentUser, Set<Integer> professionalIds) {
        Set<Long> dataInfoIds = new HashSet<>();
        for (Integer professionalId : professionalIds) {
            try {
                List<Long> userBasedRecommendedResult = dataRecommendedUtil.userBasedRecommended(currentUser, 1, professionalId, null);
                List<Long> itemBasedRecommendedResult = dataRecommendedUtil.itemBasedRecommended(currentUser, 1, professionalId, null);
                dataInfoIds.addAll(userBasedRecommendedResult);
                dataInfoIds.addAll(itemBasedRecommendedResult);
            } catch (TasteException e) {
                e.printStackTrace();
            }
        }
        return dataInfoIdsToStr(dataInfoIds);
    }

    /**
     * 将资料id集合转换为以逗号分隔的字符串
     *
     * @param dataInfoIds
     * @return
     */
    private String dataInfoIdsToStr(Set<Long> dataInfoIds) {
        StringBuilder ids = new StringBuilder();
        if(dataInfoIds != null && dataInfoIds.size() > 0) {
            for (Long id : dataInfoIds) {
                ids.append(id.toString()).append(",");
            }
            return ids.substring(0, ids.length() - 1);
        }
        return null;
    }

    /**
     * 构造关注事件推送消息
     *
     * @return
     */
    private String buildFocusMsg() {
        StringBuilder msg = new StringBuilder();
        msg.append("感谢您的关注！\n");
        msg.append("1、回复资料关键词（如机械设计），查询相关资料。\n");
        msg.append("1、回复报考专业，学校（如软件工程，中国科学技术大学），获取推荐的相关资料。\n");
        msg.append("【").append("公众号：须臾景稍绸缪").append("】");
        return msg.toString();
    }
}
